<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>2018/11/29</title>
    <!--好的代码像粥一样,是得用时间和精力熬的。-->
</head>
<body>
<script>
    //1.内层变量会覆盖外层变量
    // var tmp = new Date();
    // function f() {
    //     console.log(tmp);
    //     if (false) {
    //         var tmp = 'hello world';
    //     }
    // }
    // f(); // undefined

    //2.用来计数的循环变量泄露为全局变量
    //变量i只用来控制循环，但是循环结束后，它并没有消失，泄露成了全局变量。
    // var s = 'hello';
    // for (var i = 0; i < s.length; i++) {
    //     console.log(s[i]);
    // }
    // console.log(i); // 5


    //es6的块级作用域
    //表示外层代码块不受内层代码块的影响 如果用var 输出的结果就是10
    function f1() {
        let n = 5;
        if (true) {
            let n = 10;
        }
        console.log(n); // 5
    }
    function f2() {
        var n = 5;
        if (true) {
            var n = 10;
        }
        console.log(n); // 10
    }

    //ES6 允许块级作用域的任意嵌套。
    //使用了一个五层的块级作用域。外层作用域无法读取内层作用域的变量。
    // {{{{{let insane = 'Hello World'}}}}};

    // {{{{
    //     {let insane = 'Hello World'}
    //     console.log(insane); // 报错
    // }}}};

    //内层作用域可以定义外层作用域的同名变量。
    // {{{{
    //     let insane2 = 'Hello World';
    //    {let insane1 = 'Hello World';
    //           console.log(insane2);
    //    }
    //    console.log(insane2);
    // }}}};

//块级作用域的出现，实际上使得获得广泛应用的立即执行函数表达式（IIFE）不再必要了。
    // IIFE 写法
    // (function () {
    //     var tmp = ...;
    // ...
    // }());
    //
    // // 块级作用域写法
    // {
    //     let tmp = ...;
    // ...
    // }

    //块级作用域与函数声明
    //ES5 规定，函数只能在顶层作用域和函数作用域之中声明，不能在块级作用域声明。
    // 情况一  根据 ES5 的规定都是非法的。 不会报错。
    if (true) {
        function f() {}
    }

    // 情况二 根据 ES5 的规定都是非法的。 不会报错。
    try {
        function f() {}
    } catch(e) {
        // ...
    }

    function f() { console.log('I am outside!'); }
    (function () {
        if (false) {
            // 重复声明一次函数f
            function f() { console.log('I am inside!'); }
        }

        f();
    }());

    //允许在块级作用域内声明函数。
    // 函数声明类似于var，即会提升到全局作用域或函数作用域的头部。
    //同时，函数声明还会提升到所在的块级作用域的头部。
    //上面三条规则只对 ES6 的浏览器实现有效，其他环境的实现不用遵守，
    // 还是将块级作用域的函数声明当作let处理。
    //根据这三条规则，在浏览器的 ES6 环境中，块级作用域内声明的函数，
    // 行为类似于var声明的变量。
    // 浏览器的 ES6 环境
    function f() { console.log('I am outside!'); }

    (function () {
        if (false) {
            // 重复声明一次函数f
            function f() { console.log('I am inside!'); }
        }

        f();
    }());
    // Uncaught TypeError: f is not a function

    //考虑到环境导致的行为差异太大，应该避免在块级作用域内声明函数。
    // 如果确实需要，也应该写成函数表达式，而不是函数声明语句。
    // 函数声明语句
    {
        let a = 'secret';
        function f() {
            return a;
        }
    }
    // 函数表达式
    {
        let a = 'secret';
        let f = function () {
            return a;
        };
    }

    //ES6 的块级作用域允许声明函数的规则，只在使用大括号的情况下成立，
    // 如果没有使用大括号，就会报错。
    // 不报错
    'use strict';
    if (true) {
        function f() {}
    }
    // 报错
    'use strict';
    if (true)
        function f() {}



</script>
</body>
</html>
